/*
 * Copyright 2010 GTUG.sk
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package sk.gtug.command.server;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;

/**
 * Aspect used to replace instances of client side commands with theirs
 * appropriate server side peers during GWT deserialization process.
 * 
 * @author michal.jemala@gmail.com, igor.mihalik@gmail.com
 * 
 */
public aspect CommandAspect {

	// -- Pointcuts
	// --------------------------------------------------------------------------------
	private pointcut googleGwtOnly() 
		: within(com.google.gwt.user.server.rpc..*);

	/**
	 * Catches all calls of default constructor of all replaceable classes.
	 */
	protected pointcut constructorInstantiations()
    	: call(public (@Replaceable *).new());

	/**
	 * Catches all reflective 'newInstance' calls of all replaceable classes.
	 */
	@SuppressWarnings("unchecked")
	protected pointcut reflectiveClassInstantiations(Class c)
		: target(c) && call(* Class.newInstance()) && if(c.getAnnotation(Replaceable.class) != null);

	/**
	 * Catches all reflective 'newInstance' calls on constructors of all
	 * replaceable classes.
	 */
	@SuppressWarnings("unchecked")
	protected pointcut reflectiveConstructorInstantiations(Constructor c)
        : target(c) && call(* Constructor.newInstance(..)) && if(c.getDeclaringClass().getAnnotation(Replaceable.class) != null);

	// -- Advices
	// ----------------------------------------------------------------------------------

	// Object around() : constructorInstantiations() && googleGwtOnly() {
	// return
	// doReplace(thisJoinPointStaticPart.getSignature().getDeclaringType());
	// }

	// @SuppressWarnings("unchecked")
	// Object around(Class c) : reflectiveClassInstantiations(c) &&
	// googleGwtOnly() {
	// return doReplace(c);
	// }

	@SuppressWarnings("unchecked")
	Object around(Constructor c) : reflectiveConstructorInstantiations(c)  && googleGwtOnly() {
		return doReplace(c.getDeclaringClass());
	}

	/*
	 * @SuppressWarnings("unchecked") Object around(Class c1, Class c2, Object
	 * o) : deserializeMethodContext() && deserializeImplMethodCall(c1, c2, o)
	 * && googleGwtOnly() { return proceed(c1, o.getClass(), o); }
	 */

	// -- Business method
	// --------------------------------------------------------------------------
	protected Object doReplace(Class<?> replacer) {
		Class<?> replacee = replacer.getAnnotation(Replaceable.class).value();
		try {
			Constructor<?> c = replacee.getDeclaredConstructor();
			if ((!Modifier.isPublic(c.getModifiers()) || !Modifier.isPublic(c
					.getDeclaringClass().getModifiers()))
					&& !c.isAccessible()) {
				c.setAccessible(true);
			}
			return c.newInstance();
		} catch (Exception e) {
			throw new Error(e);
		}
	}

}